/*******************************************************************************
** www.IfcGears.com                                                           **
** Copyright (C) 2010 - Bauhaus University Weimar, www.uni-weimar.de          **
** Written: 2010-10-02, Fabian Gerold, www.fabiangerold.de                    **
*******************************************************************************/

#include <assert.h>
#include <fstream>
#include <iostream>

#include "schema/entity/Attribute.h"
#include "schema/entity/Entity.h"
#include "schema/type/Type.h"
#include "schema/CodeFormat.h"
#include "IfcGears.h"

IfcGears::IfcGears()
{
	m_basic_types.insert( std::pair<QString,QString>( QString("INTEGER"),	QString("int") ) );
	m_basic_types.insert( std::pair<QString,QString>( QString("REAL"),		QString("double") ) );
	m_basic_types.insert( std::pair<QString,QString>( QString("NUMBER"),	QString("int") ) );
	m_basic_types.insert( std::pair<QString,QString>( QString("STRING"),	QString("std::string") ) );
	m_basic_types.insert( std::pair<QString,QString>( QString("BOOLEAN"),	QString("bool") ) );
	m_basic_types.insert( std::pair<QString,QString>( QString("LOGICAL"),	QString("bool") ) );
	m_basic_types.insert( std::pair<QString,QString>( QString("BINARY"),	QString("const char*") ) );

	// simple types like 
	//TYPE IfcAreaMeasure = REAL;
	//END_TYPE;
	// could be handled like 
	//typedef IfcAreaMeasure double;
	// and inititialized with NAN 

	m_filename_chars = "a-zA-Z0-9_\\-";
	
//	setNamespace( "" );
	m_include_comments = true;
	m_method_setEntity = false;
	
	m_header_comment = 
"/* -*-c++-*- IfcGears - www.ifcgears.com - Copyright (C) 2011 Fabian Gerold\n\
 *\n\
 * This library is open source and may be redistributed and/or modified under  \n\
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or \n\
 * (at your option) any later version.  The full license is in LICENSE file\n\
 * included with this distribution, and on the openscenegraph.org website.\n\
 * \n\
 * This library is distributed in the hope that it will be useful,\n\
 * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the \n\
 * OpenSceneGraph Public License for more details.\n\
*/\n";

	m_ifc_release = shared_ptr<IfcRelease>(new IfcRelease() );
	m_ifc_namespace = false;
	//m_additional_parser.insert( std::pair<QString,QString>( "IfcProject", "\t\tm_model->setIfcProject( obj );\n" ) );
}

void IfcGears::setPathOut( QString path_out )
{
	if( !path_out.endsWith( "/" ) )
	{
		path_out = path_out + "/";
	}
	m_path_out = path_out;
}

void IfcGears::parseSchemaFiles( QStringList content_in )
{
	//m_ifc_releases.clear();
	m_ifc_release->m_map_entities.clear();
	m_ifc_release->m_map_types.clear();
	
	// read schema files
	foreach( QString content, content_in )
	{
		parseSchema( content );
	}

	processTypesAndEntities();

	// write out files
	writeIfcClassFiles();

	emit( signalTxtOut( "writing parser" ) );
	writeReaderEntities();
	
	emit( signalProgressValue( 1000 ) );
	emit( signalTxtOut( "done" ) );
}

void IfcGears::parseSchema( QString content )
{
	// remove comments
	QRegExp rx_comment1( "\\(\\*(.|[\r\n])*\\*\\)" );
	rx_comment1.setMinimal( true );
	content.remove( rx_comment1 );

	// remove single line comments
	content.remove( QRegExp( "--[^\n]*" ) );

	QString schema_version;
	QRegExp rx_schema( "SCHEMA\\s*([a-zA-Z0-9_]+);" );
	if( rx_schema.indexIn( content, 0 ) >= 0 )
	{
		QString detected_schema = rx_schema.cap( 1 );

		QRegExp rx_major_minor( "IFC([0-9])+X([0-9])+" );
		if( rx_major_minor.indexIn( detected_schema, 0 ) >= 0 )
		{
			QString ifc_major = rx_major_minor.cap( 1 );
			QString ifc_minor = rx_major_minor.cap( 2 );
		
			detected_schema = "IFC" + ifc_major + "X" + ifc_minor;
		}
		schema_version = detected_schema;
		content.remove( rx_schema );
		emit( signalTxtOut( "detected schema version " + detected_schema ) );
	}
	else
	{
		emit( signalTxtOutWarning( "parseSchema: no schema version detected " ) );
	}
	
	emit( signalTxtOut( "extracting types and entities from schema" ) );
	int complete_size = content.size();
	findTypes( content, complete_size, schema_version );
	findEntities( content, complete_size, schema_version );
	findFunctions( content, complete_size, schema_version );
	
	QRegExp rx_schema_end( "END_SCHEMA;" );
	if( rx_schema_end.indexIn( content, 0 ) < 0 )
	{
		emit( signalTxtOutWarning( "no END_SCHEMA; statement" ) );
	}
	content.remove( QRegExp( "END_SCHEMA;" ) );
	content.remove( QString( "\n" ) );

	if( content.size() > 0 )
	{
		emit( signalTxtOutWarning( "remaining schema:\n" + content ) );
	}

	return;
}

void IfcGears::processTypesAndEntities()
{
	std::map<QString,shared_ptr<Type> >::iterator it_types;
	std::map<QString,shared_ptr<Entity> >::iterator it_entities;

	emit( signalTxtOut( "parsing" ) );
	size_t num_types = 0;
	size_t num_entities = 0;

	size_t num_types_and_entities = 0;
	std::map<QString,shared_ptr<IfcRelease> >::iterator it_releases;
//	for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;
		num_types_and_entities += m_ifc_release->m_map_types.size();
		num_types_and_entities += m_ifc_release->m_map_entities.size();
	}

	//for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;

		for( it_types = m_ifc_release->m_map_types.begin(); it_types != m_ifc_release->m_map_types.end(); ++it_types )
		{
			shared_ptr<Type>  t = (*it_types).second;
			try
			{
				t->parse();
			}
			catch( std::exception& e )
			{
				emit( signalTxtOutWarning( e.what() ) );
			}
		
			++num_types;
			double progress = (double)(num_types+num_entities)/(num_types_and_entities);
			emit( signalProgressValue( 100+(int)(progress*100) ) );
		}

		for( it_entities = m_ifc_release->m_map_entities.begin(); it_entities != m_ifc_release->m_map_entities.end(); ++it_entities )
		{
			shared_ptr<Entity>  e = (*it_entities).second;
			try
			{
				e->parse();
			}
			catch( std::exception& err )
			{
				emit( signalTxtOutWarning( err.what() ) );
			}

			++num_entities;
			double progress = (double)(num_types+num_entities)/(num_types_and_entities);
			emit( signalProgressValue( 200+(int)(progress*800) ) );
		}


		for( it_types = m_ifc_release->m_map_types.begin(); it_types != m_ifc_release->m_map_types.end(); ++it_types )
		{
			shared_ptr<Type>  t = (*it_types).second;
			t->linkDependencies();
		}

		for( it_entities = m_ifc_release->m_map_entities.begin(); it_entities != m_ifc_release->m_map_entities.end(); ++it_entities )
		{
			shared_ptr<Entity>  e = (*it_entities).second;
			e->linkDependencies();
		}
	}

	emit( signalTxtOut( "processed types: " + QString::number( num_types ) ) );
	emit( signalTxtOut( "processed entities: " + QString::number( num_entities ) ) );
}

void IfcGears::slotEmitTxtOut( QString txt )
{
	emit( signalTxtOut( txt ) );
}
void IfcGears::slotEmitTxtOutWarning( QString txt )
{
	emit( signalTxtOutWarning( txt ) );
}

bool IfcGears::findTypes( QString& content, int complete_size, QString schema_version )
{
	int pos_type_begin=0;
	QRegExp rx_type_begin( "\\bTYPE\\s+([a-zA-Z0-9_]+)\\s*=" );

	while( (pos_type_begin = rx_type_begin.indexIn( content, pos_type_begin ) ) != -1 )
	{
		QString complete_match = rx_type_begin.cap(0);
		QString type_name = rx_type_begin.cap(1);
		QString schema;

		int pos_type_end;
		QRegExp rx_type_end( "END_TYPE;[\\r]*\\n*" );
		rx_type_end.setMinimal( true );
		if( (pos_type_end = rx_type_end.indexIn( content, pos_type_begin ) ) == -1 )
		{
			emit( signalTxtOutWarning( "findTypes: no END_TYPE found" ) );
			break;
		}
		int length_type = pos_type_end - pos_type_begin;
		if( length_type > 0 )
		{
			int length_type_end = rx_type_end.matchedLength();
			QStringRef type_str_ref(&content, pos_type_begin, length_type);
			QString type_str = type_str_ref.toString();
			schema = type_str;
			content.remove( pos_type_begin, length_type + length_type_end );
		}
		else
		{
			emit( signalTxtOutWarning( "parseSchema: length_type == 0" ) );
			break;
		}

		shared_ptr<Type>  type( new Type( this ) );
		type->m_classname = type_name;
		type->setSchema( schema );
		
		//shared_ptr<IfcRelease> ifc_release;
		//if( m_ifc_releases.find(schema_version) == m_ifc_releases.end() )
		//{
		//	m_ifc_releases.insert( std::pair<QString,shared_ptr<IfcRelease> >(schema_version, shared_ptr<IfcRelease>(new IfcRelease() ) ) );
		//}
		//ifc_release = m_ifc_releases[schema_version];
		type->setSchemaVersion( schema_version, m_ifc_release );

		//QString key = schema_version + "::" + type_name;
		QString key = type_name;
		if( m_ifc_release->m_map_types.find( key ) != m_ifc_release->m_map_types.end() )
		{
			emit( signalTxtOutWarning( "parseSchema: type " + type_name + " already in m_map_types" ) );
			break;
		}
		m_ifc_release->m_map_types.insert( std::pair<QString,shared_ptr<Type> >(key,type ) );

		double progress = (double)(complete_size-content.size())/(double)complete_size;
		emit( signalProgressValue( (int)(progress*40.0) ) );
	}
	return true;
}

bool IfcGears::findEntities( QString& content, int complete_size, QString schema_version )
{
	// first pass, collect all type names
	int pos_entity_begin=0;
	QRegExp rx_entity_begin( "\\bENTITY\\s+([a-zA-Z0-9_]+)\\s*" );

	while( (pos_entity_begin = rx_entity_begin.indexIn( content, pos_entity_begin ) ) != -1 )
	{
		QString complete_match = rx_entity_begin.cap(0);
		QString entity_name = rx_entity_begin.cap(1);
			
		QString schema;
		int pos_entity_end;
		QRegExp rx_entity_end( "END_ENTITY;[\\r]*\\n*" );
		rx_entity_end.setMinimal( true );
		if( (pos_entity_end = rx_entity_end.indexIn( content, pos_entity_begin ) ) == -1 )
		{
			emit( signalTxtOutWarning( "findEntities: no END_ENTITY found" ) );
			break;
		}
		int length_entity = pos_entity_end - pos_entity_begin;
		if( length_entity > 0 )
		{
			int length_entity_end = rx_entity_end.matchedLength();
			QStringRef entity_str_ref(&content, pos_entity_begin, length_entity);
			QString entity_str = entity_str_ref.toString();
			schema = entity_str;
			content.remove( pos_entity_begin, length_entity + length_entity_end );
		}
		else
		{
			emit( signalTxtOutWarning( "findEntities: length_entity == 0" ) );
			break;
		}

		shared_ptr<Entity>  entity( new Entity( this ) );
		entity->m_classname = entity_name;
		entity->setSchema( schema );
		

		//shared_ptr<IfcRelease> ifc_release;
		//if( m_ifc_releases.find(schema_version) == m_ifc_releases.end() )
		//{
		//	m_ifc_releases.insert( std::pair<QString,shared_ptr<IfcRelease> >(schema_version, shared_ptr<IfcRelease>(new IfcRelease() ) ) );
		//}
		//ifc_release = m_ifc_releases[schema_version];
		entity->setSchemaVersion( schema_version, m_ifc_release );

		//QString key = schema_version + "::" + entity_name;
		QString key = entity_name;
		if( m_ifc_release->m_map_entities.find( key ) != m_ifc_release->m_map_entities.end() )
		{
			emit( signalTxtOutWarning( "findEntities: Entity " + entity_name + " already in map"  ) );
		}
		m_ifc_release->m_map_entities.insert( std::pair<QString,shared_ptr<Entity> >(key,entity ) );
	
		double progress = (double)(complete_size-content.size())/(double)complete_size;
		emit( signalProgressValue( 40+(int)(progress*60.0) ) );
	}
	return true;
}

void IfcGears::writeFile( QString path, QString& content )
{
	std::stringstream sstr;
	CodeFormat::format_indentation( content.toAscii().constData(), sstr );
	QString formatted_content = sstr.str().c_str();

	// check if file content has changed
	if( QFile::exists(m_path_out + path))
	{
		QFile file_existing( m_path_out + path );
		file_existing.open( QFile::ReadOnly );

		QByteArray ba_existing = file_existing.readAll();
		ba_existing = ba_existing.replace( "\r\n", "\n");
		QString qstr_exsisting( ba_existing );
		
		if( qstr_exsisting.compare(formatted_content)== 0)
		{
			// content is equal
			emit( signalTxtOut( "skipped file (no changes):\t" + m_path_out + path ) );
			file_existing.close();
			return;
		}
	}


	QFile file_out( m_path_out + path );
	QFileInfo file_out_info( m_path_out + path );

	QDir abs_dir = file_out_info.absoluteDir();
	QString abs_file_path = file_out_info.absoluteFilePath();
	QString abs_path = file_out_info.absolutePath();

	if( !abs_dir.exists( abs_path ) )
	{
		if( !abs_dir.mkpath( abs_path ) )
		{
			emit( signalTxtOutWarning( "couldn't create dir " + abs_file_path ) );
			return;
		}
	}

	if( !file_out.open(QIODevice::WriteOnly | QIODevice::Text) )
	{
		emit( signalTxtOutWarning( "couldn't write file " + path ) );
		return;
	}

	QTextStream file_out_stream( &file_out );
	file_out_stream << sstr.str().c_str();
	file_out.close();

	emit( signalTxtOut( "file written:\t" + path ) );
}


bool IfcGears::writeIfcClassFiles()
{
	std::map<QString,shared_ptr<Type> >::iterator it_types;
	std::map<QString,shared_ptr<Entity> >::iterator it_entities;

	emit( signalTxtOut( "generating classes" ) );

	int processed = 0;
	int num_process = 0;
	std::map<QString,shared_ptr<IfcRelease> >::iterator it_releases;
	//for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;
		num_process += (int)(m_ifc_release->m_map_entities.size());
		num_process += (int)(m_ifc_release->m_map_types.size());
	}

	// write the header file
	//for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//QString ifc_release_name = (*it_releases).first;
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;

		QString content_header_all_ifc_includes( m_header_comment );
		content_header_all_ifc_includes += "#pragma once\n";

		for( it_types = m_ifc_release->m_map_types.begin(); it_types != m_ifc_release->m_map_types.end(); ++it_types )
		{
			shared_ptr<Type>  t = (*it_types).second;

			// h file
			QString content_h = t->getCodeHeader();
			//writeFile( ifc_release_name + "/include/" + t->m_classname + ".h", content_h );
			writeFile( "IFC2X4/include/" + t->m_classname + ".h", content_h );

			// cpp file
			QString content_cpp( t->getCodeCpp() );
			//writeFile( ifc_release_name + "/" + t->m_classname + ".cpp", content_cpp );
			writeFile( "IFC2X4/" + t->m_classname + ".cpp", content_cpp );

			// all include file
			content_header_all_ifc_includes += "#include \"" + t->m_classname + ".h\"\n";

			QString class_name = t->m_classname;
			QString reader_args = "std::string& arg";

			if( t->m_reader_needs_object_map )
			{
				QString namesp;
				//if( m_namespace.size() > 0 )namesp = m_namespace + "::";
				reader_args = "std::string& arg, const std::map<int,shared_ptr<" + namesp + "IfcGearsEntity> >& map";
			}

			++processed;
			emit( signalProgressValue( ((double)processed/(num_process)*1000) ) );

		}
		//writeFile( ifc_release_name + "/include/" + ifc_release_name + "Types.h", content_header_all_ifc_includes );
		writeFile( "IFC/Types.h", content_header_all_ifc_includes );
	}

	// entities
	//for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//QString ifc_release_name = (*it_releases).first;
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;

		QString all_entity_includes( m_header_comment );
		all_entity_includes += "#pragma once\n";

		for( it_entities = m_ifc_release->m_map_entities.begin(); it_entities != m_ifc_release->m_map_entities.end(); ++it_entities )
		{
			shared_ptr<Entity>  e = (*it_entities).second;

			// h file
			QString content_h = e->getCodeHeader();
			//writeFile( ifc_release_name + "/include/" + e->m_classname + ".h", content_h );
			writeFile( "IFC2X4/include/" + e->m_classname + ".h", content_h );

			// cpp file
			QString content_cpp( e->getCodeCpp() );
			//writeFile( ifc_release_name + "/" + e->m_classname + ".cpp", content_cpp );
			writeFile( "IFC2X4/" + e->m_classname + ".cpp", content_cpp );

			// all include file
			all_entity_includes += "#include \"ifcgears/IFC2X4/include/" + e->m_classname + ".h\"\n";

			++processed;
			emit( signalProgressValue( ((double)processed/(num_process)*1000) ) );
		}

		//writeFile( ifc_release_name + "/include/" + ifc_release_name + "Entities.h", content_header_all_ifc_entity_includes );
		writeFile( "IFC/Entities.h", all_entity_includes );
	}
	return true;
}

void IfcGears::writeCMakeFile()
{
	QString content_cmake_file;
	//content_cmake_file += "INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)\n";
	//content_cmake_file += "# .cxx sources\n";
	content_cmake_file += "list(APPEND ifcgears_CXX_SRC\n";
	

	//content_cmake_file += "./ifcgears/guid/CreateGuid_64.c\n";
	//content_cmake_file += "./ifcgears/model/IfcGearsModel.cpp\n";
	//content_cmake_file += "./ifcgears/model/IfcGearsObject.cpp\n";
	//content_cmake_file += "./ifcgears/model/UnitConverter.cpp\n";
	//content_cmake_file += "./ifcgears/reader/IfcGearsReader.cpp\n";
	//content_cmake_file += "./ifcgears/reader/IfcGearsReaderException.cpp\n";
	//content_cmake_file += "./ifcgears/reader/IfcStepReader.cpp\n";
	//content_cmake_file += "./ifcgears/reader/IfcXmlReader.cpp\n";
	//content_cmake_file += "./ifcgears/reader/ReaderUtil.cpp\n";
	//content_cmake_file += "./ifcgears/writer/IfcStepWriter.cpp\n";
	//content_cmake_file += "./ifcgears/writer/IfcXmlWriter.cpp\n";
	//content_cmake_file += "./ifcgears/writer/WriterUtil.cpp\n";
	//content_cmake_file += "./ifcgears/xml/XmlTools.cpp\n";

	//content_cmake_file += "./ifcgears/"+m_ifc_classes_subfolder+"/AllIfcEntityReaders.cpp\n";
	
		

	emit( signalTxtOut( "generating cmake file" ) );

	// write the header file

	std::map<QString,shared_ptr<IfcRelease> >::iterator it_releases;
	//for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//QString ifc_release_name = (*it_releases).first;
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;
		
		content_cmake_file += "IFC/EntityBroker.cpp\n";

		std::map<QString,shared_ptr<Type> >::iterator it_types;
		for( it_types = m_ifc_release->m_map_types.begin(); it_types != m_ifc_release->m_map_types.end(); ++it_types )
		{
			shared_ptr<Type>  t = (*it_types).second;
			content_cmake_file += t->m_classname + ".cpp\n";
		}

		std::map<QString,shared_ptr<Entity> >::iterator it_entities;
		for( it_entities = m_ifc_release->m_map_entities.begin(); it_entities != m_ifc_release->m_map_entities.end(); ++it_entities )
		{
			shared_ptr<Entity>  e = (*it_entities).second;
			content_cmake_file += e->m_classname + ".cpp\n";
		}
	}

	content_cmake_file += ")\n";
	content_cmake_file += "set(ifcgears_CXX_SRC \"${ifcgears_CXX_SRC}\" PARENT_SCOPE)\n";
	//content_cmake_file += "INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})\n";
	//content_cmake_file += "# create library\n";
	//content_cmake_file += "ADD_LIBRARY(ifcgears STATIC ${ifcgears_CXX_SRC})\n";

	writeFile( "CMakeLists.txt", content_cmake_file );
}


void IfcGears::writeReaderEntities()
{
	//QString enum_all( "namespace " + ifc_release_name + "\n{\n" );
	QString enum_all( m_header_comment );
	enum_all += "#pragma once\n";
	enum_all += "enum IfcEntityEnum\n{\n";

	QString content_parser_cpp( m_header_comment );
	content_parser_cpp += "\n#include <vector>\n";
	content_parser_cpp += "#include <map>\n";
	content_parser_cpp += "#include <string>\n";

	QString map_entity_enum_init( "static const std::pair<std::string, IfcEntityEnum> initializers_entity[] = {\n" );
	QString switch_case_obj_only;

	QString content_entitymap_h( m_header_comment );
	content_entitymap_h += "#pragma once\n";
	content_entitymap_h += "#include <map>\n";


	std::map<QString,shared_ptr<Entity> >::iterator it_entities;
	std::map<QString,shared_ptr<IfcRelease> >::iterator it_releases;
	//for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		//QString ifc_release_name = (*it_releases).first;
		//shared_ptr<IfcRelease> ifc_release = (*it_releases).second;

		for( it_entities = m_ifc_release->m_map_entities.begin(); it_entities != m_ifc_release->m_map_entities.end(); ++it_entities )
		{
			shared_ptr<Entity>  e = (*it_entities).second;

			//if( it_releases != m_ifc_releases.begin() && it_entities != ifc_release->m_map_entities.begin() )
			{
				//enum_all += ",\n";
				//map_entity_enum_init += ",\n";
			}
			QString class_name_upper = e->m_classname.toUpper();
			//enum_all += ifc_release_name + "_" + class_name_upper + ",\n";
			enum_all += class_name_upper + ",\n";
			map_entity_enum_init += "std::pair<std::string, IfcEntityEnum>(\"" + class_name_upper + "\", " + class_name_upper + "),\n";

			//switch_case_obj_only += "case " + ifc_release_name + "_" + class_name_upper + ":" + "return new " + ifc_release_name + "::" + e->m_classname + "();\n";
			switch_case_obj_only += "case " + class_name_upper + ":" + "return new " + e->m_classname + "();\n";
		}
		
		content_parser_cpp += "#include \"Entities.h\"\n";
		content_parser_cpp += "#include \"EntityEnums.h\"\n";
		//content_parser_cpp += "#include \"" + ifc_release_name + "/include/" + ifc_release_name + "Entities.h\"\n";
		//content_parser_cpp += "#include \"" + ifc_release_name + "/include/" + ifc_release_name + "EntityEnums.h\"\n";

		//content_entitymap_h += "#include \"" + ifc_release_name + "EntityEnums.h\"\n";
		content_entitymap_h += "#include \"EntityEnums.h\"\n";
	}

	enum_all.remove( enum_all.size()-2, 2 );// = ",\n";
	map_entity_enum_init.remove( map_entity_enum_init.size()-2, 2 ); //+= ",\n";
	map_entity_enum_init += "};\n";

	content_parser_cpp += "#include \"ifcgears/model/IfcGearsException.h\"\n\n";

	//content_parser_cpp += "namespace " + ifc_release_name + "\n{\n";
	content_parser_cpp += "IfcGearsEntity* createIfcEntity( const IfcEntityEnum entity_enum )\n{\n";
	content_parser_cpp += "switch( entity_enum )\n{\n";
	content_parser_cpp += switch_case_obj_only + "\n";
	content_parser_cpp += "default: throw IfcGearsException(\"given IfcEntityEnum not valid\");\n";
	content_parser_cpp +="}\n";
	content_parser_cpp +="return 0;\n";
	content_parser_cpp +="}\n";
	//content_parser_cpp +="}\n";

	if( !m_include_comments )
	{
		content_parser_cpp.remove( QRegExp("//[^\n]*") );
		content_parser_cpp.replace( QRegExp("\n\\s*\n"), "\n" );
	}

	//writeFile( ifc_release_name + "/" + ifc_release_name + "EntityReaders.cpp", content_parser_cpp );
	writeFile( "IFC/EntityBroker.cpp", content_parser_cpp );

	// write class map in header file
	enum_all += "\n};\n\n";

	if( !m_include_comments )
	{
		enum_all.remove( QRegExp("//[^\n]*") );
		enum_all.replace( QRegExp("\n\\s*\n"), "\n" );
	}

	//writeFile( ifc_release_name + "/include/" + ifc_release_name + "EntityEnums.h", content_entity_enums_h );
	writeFile( "IFC/EntityEnums.h", enum_all );

			//content_entitymap_h += "namespace " + ifc_release_name + "\n{\n";
	content_entitymap_h += map_entity_enum_init + "\n";
	//content_entitymap_h +="}\n";
		
	if( !m_include_comments )
	{
		content_entitymap_h.remove( QRegExp("//[^\n]*") );
		content_entitymap_h.replace( QRegExp("\n\\s*\n"), "\n" );
	}

	//writeFile( ifc_release_name + "/include/" + ifc_release_name + "EntitiesMap.h", content_entitymap_h );
	writeFile( "IFC/EntitiesMap.h", content_entitymap_h );
	
}

/*
void IfcGears::writeReaderTypes()
{
	QString enum_all;
	//enum_all += "namespace " + ifc_release_name + "\n{\n";
	enum_all += "enum IfcTypeEnum\n{\n";

	QString map_all_init( "static const std::pair<std::string, IfcTypeEnum> initializers_type[] = {\n" );
	QString switch_case_obj_only;

	QString content_parser_cpp( m_header_comment );
	content_parser_cpp += "\n#include <vector>\n";
	content_parser_cpp += "#include <map>\n";
	content_parser_cpp += "#include <string>\n";

	std::map<QString,shared_ptr<Type> >::iterator it_types;
	std::map<QString,shared_ptr<IfcRelease> >::iterator it_releases;
	for( it_releases = m_ifc_releases.begin(); it_releases != m_ifc_releases.end(); ++it_releases )
	{
		QString ifc_release_name = (*it_releases).first;
		shared_ptr<IfcRelease> ifc_release = (*it_releases).second;

		
		

		for( it_types = ifc_release->m_map_types.begin(); it_types != ifc_release->m_map_types.end(); ++it_types )
		{
			shared_ptr<Type>  t = (*it_types).second;

			if( it_types != ifc_release->m_map_types.begin() )
			{
				enum_all += ",\n";
				map_all_init += ",\n";
			}
			QString class_name = t->m_classname.toUpper();
			enum_all += ifc_release_name + "_" + class_name;
			map_all_init += "std::pair<std::string, IfcTypeEnum>(\"" + class_name + "\", " + class_name + ")";

			if( t->m_select.size() > 0 )
			{
				// abstract class, no object can be created
			}
			else
			{
				switch_case_obj_only += "case " + class_name + ":" + "return new " + ifc_release_name + "::" + t->m_classname + "();\n";
			}
		}
		map_all_init += "};\n";

		content_parser_cpp += "#include \""+ifc_release_name+"/include/" + ifc_release_name + "Types.h\"\n";
		content_parser_cpp += "#include \""+ifc_release_name+"/include/" + ifc_release_name + "TypesMap.h\"\n";

	}
		
		
		content_parser_cpp += "#include \"model/IfcGearsException.h\"\n\n";

		//content_parser_cpp += "namespace " + ifc_release_name + "\n{\n";
		content_parser_cpp += "IfcGearsType* readIfcType( const IfcTypeEnum type_enum )\n{\n";
		content_parser_cpp += "switch( type_enum )\n{\n";
		content_parser_cpp += switch_case_obj_only + "\n";
		content_parser_cpp += "default: throw IfcGearsException(\"given IfcTypeEnum not valid\");\n";
		content_parser_cpp +="}\n";
		content_parser_cpp +="return 0;\n";
		content_parser_cpp +="}\n";
		//content_parser_cpp +="}\n";
		
		if( !m_include_comments )
		{
			content_parser_cpp.remove( QRegExp("//[^\n]*") );
			content_parser_cpp.replace( QRegExp("\n\\s*\n"), "\n" );
		}

	

	//writeFile( ifc_release_name + "/" + ifc_release_name + "TypeReaders.cpp", content_parser_cpp );
	writeFile( "IFC/TypeReaders.cpp", content_parser_cpp );

	enum_all += "\n};\n";
	enum_all += "}\n";


	// write class map in header file
	QString content_parser_typemap_h( m_header_comment );
	content_parser_typemap_h += "#pragma once\n";
	content_parser_typemap_h += "#include <map>\n";
	content_parser_typemap_h += enum_all + map_all_init + "\n";

	if( !m_include_comments )
	{
		content_parser_typemap_h.remove( QRegExp("//[^\n]*") );
		content_parser_typemap_h.replace( QRegExp("\n\\s*\n"), "\n" );
	}

	writeFile( "IFC/include/TypesMap.h", content_parser_typemap_h );
}
*/

bool IfcGears::findFunctions( QString& file_content, int complete_size, QString schema_version )
{
	int pos_type_begin=0;
	QRegExp rx_type_begin( "\\b(FUNCTION|RULE)\\s([a-zA-Z0-9_]*)[\\s]*" );
	while( (pos_type_begin = rx_type_begin.indexIn( file_content, pos_type_begin ) ) != -1 )
	{
		QString complete_match = rx_type_begin.cap(0);
		QString type_name = rx_type_begin.cap(2);

		int pos_type_end;
		QRegExp rx_type_end( "END_(FUNCTION|RULE);[\\r]*\\n" );
		rx_type_end.setMinimal( true );
		if( (pos_type_end = rx_type_end.indexIn( file_content, pos_type_begin ) ) == -1 )
		{
			break;
			assert( 0 );
		}
		int length_type = pos_type_end - pos_type_begin;
		if( length_type > 0 )
		{
			int length_type_end = rx_type_end.matchedLength();

			QStringRef type_str_ref(&file_content, pos_type_begin, length_type);
			QString type_str = type_str_ref.toString();

			file_content.remove( pos_type_begin, length_type + length_type_end );
		}
		else
		{
			assert( 0 );
		}
	}
	return true;
}
